import type { HttpData, HttpRequestConfig, HttpResponse, HttpTask } from 'luch-request'
import Request from 'luch-request'
import { assign } from 'lodash-es'
import type { ApiResult, ApiResultOf } from './../../services/api/apiResult'
import { Toast } from '@/utils/uniapi/prompt'
import { getBaseUrl } from '@/utils/env'
import { useAuthStore } from '@/state/modules/auth'
import { ResultEnum } from '@/enums/httpEnum'

const BASE_URL = getBaseUrl()
const HEADER = {
  'Content-Type': 'application/json;charset=UTF-8;',
  'Accept': 'application/json, text/plain, */*',
}

type RequestMethod = 'get' | 'post' | 'put' | 'delete'

interface RequestParam {
  url: string
  method?: RequestMethod
  data?: HttpData
  config?: HttpRequestConfig<UniApp.RequestTask>
}

function createRequest() {
  return new Request({
    baseURL: BASE_URL,
    header: HEADER,
    custom: {
      auth: true,
    },
  })
}

const luchRequest = createRequest()

/**
 * 请求拦截器
 */
luchRequest.interceptors.request.use(
  (options) => {
    if (options.custom?.auth) {
      const authStore = useAuthStore()
      if (!authStore.isLogin) {
        Toast('请先登录')
        // token不存在跳转到登录页
        return Promise.reject(options)
      }
      options.header = assign(options.header, {
        authorization: `Bearer ${authStore.getToken}`,
      })
    }
    return options
  },
  options => Promise.reject(options),
)

/**
 * 响应拦截器
 */
luchRequest.interceptors.response.use(
  // onFulfilled，对响应成功做点什么 可使用async await 做异步操作
  async (response) => {
    const { data, statusCode } = response
    // 正常的http错误响应
    if (statusCode !== 200) {
      Toast(response.errMsg)
      response.data = { error: 1, message: response.errMsg }
      return Promise.reject(response)
    }

    // 自定义的错误响应
    // 解构响应data
    const { code, message } = data as ApiResult
    // 自定义响应成功
    if (code === ResultEnum.SUCCESS || code === ResultEnum.CUSTOM_SUCCESS)
      return Promise.resolve(response)

    // 自定义响应错误时，如果有消息，则输出
    message && Toast(message)
    return Promise.reject(response)
  },
  // onRejected，对响应错误做点什么 （statusCode !== 200）
  (error) => {
    return Promise.reject(error)
  },
)

async function getRequestResponse<T>(param: {
  method: RequestMethod
  url: string
  data?: HttpData
  config?: HttpRequestConfig<UniApp.RequestTask>
}): Promise<HttpResponse<ApiResultOf<T>, HttpTask>> {
  const { method, url, data, config } = param
  let res: HttpResponse<ApiResultOf<T>, HttpTask>
  if (method === 'get' || method === 'delete')
    res = await luchRequest[method]<ApiResultOf<T>>(url, config)
  else
    res = await luchRequest[method]<ApiResultOf<T>>(url, data, config)
  return res
}

/**
 * 异步promise请求
 * @param param - 请求参数
 * - url: 请求地址
 * - method: 请求方法(默认get)
 * - data: 请求的body的data
 * - axiosConfig: axios配置
 */
async function asyncRequest<T>(param: RequestParam): Promise<Service.RequestResult<T>> {
  const { url } = param
  const method = param.method || 'get'
  const { data: responseData, statusCode, errMsg } = (await getRequestResponse<T>({
    method,
    url,
    data: param.data,
    config: param.config,
  }))
  let res: Service.RequestResult<T>
  if (statusCode !== ResultEnum.SUCCESS) {
    res = { error: { code: statusCode, msg: errMsg, type: 'http' }, data: null }
  }
  else {
    const { code, data, message } = responseData
    if (code === ResultEnum.SUCCESS || code === ResultEnum.CUSTOM_SUCCESS)
      res = { error: null, data: data as T }
    else
      res = { error: { code, msg: message as string, type: 'backend' }, data: null }
  }

  return res
}

function createCustomRequest() {
  function get<T>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>) {
    return asyncRequest<T>({ url, method: 'get', config })
  }

  function post<T>(url: string, data?: HttpData, config?: HttpRequestConfig<UniApp.RequestTask>) {
    return asyncRequest<T>({ url, method: 'post', data, config })
  }

  function put<T>(url: string, data?: HttpData, config?: HttpRequestConfig<UniApp.RequestTask>) {
    return asyncRequest<T>({ url, method: 'put', data, config })
  }

  function deleteHandle<T>(url: string, data?: HttpData, config?: HttpRequestConfig<UniApp.RequestTask>) {
    return asyncRequest<T>({ url, method: 'delete', data, config })
  }

  return {
    get,
    post,
    put,
    delete: deleteHandle,
  }
}

export const request = createCustomRequest()
